﻿using SharpGL.Media3D;

namespace SharpGL.Triangulator.Geometry
{
    /// <summary>
    /// triangles made from three point indexes
    /// </summary>
    public class Triangle
    {
        #region Private Fields

        /// <summary>
        /// First vertex index in triangle
        /// </summary>
        private Point3D firstPoint;

        /// <summary>
        /// Second vertex index in triangle
        /// </summary>
        private Point3D secondPoint;

        /// <summary>
        /// Third vertex index in triangle
        /// </summary>
        private Point3D thirdPoint;

        #endregion Private Fields

        #region Public Constructors

        /// <summary>
        /// Initializes a new instance of a triangle
        /// </summary>
        /// <param name="firstPoint">Vertex 1</param>
        /// <param name="secondPoint">Vertex 2</param>
        /// <param name="thirdPoint">Vertex 3</param>
        public Triangle(Point3D firstPoint, Point3D secondPoint, Point3D thirdPoint)
        {
            FirstPoint = firstPoint;
            SecondPoint = secondPoint;
            ThirdPoint = thirdPoint;
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Returns true if the point (point) lies inside the circumcircle made up by triangle points
        /// </summary>
        /// <remarks>
        /// NOTE: A point on the edge is inside the circumcircle
        /// </remarks>
        /// <param name="point">Point to check</param>
        /// <returns>true if point is inside circle</returns>
        public bool ContainsInCircumcircle(Point3D point)
        {
            /*double ax = this.firstPoint.X - point.X;
            double ay = this.firstPoint.Y - point.Y;
            double bx = this.secondPoint.X - point.X;
            double by = this.secondPoint.Y - point.Y;
            double cx = this.thirdPoint.X - point.X;
            double cy = this.thirdPoint.Y - point.Y;

            double det_ab = ax * by - bx * ay;
            double det_bc = bx * cy - cx * by;
            double det_ca = cx * ay - ax * cy;

            double a_squared = ax * ax + ay * ay;
            double b_squared = bx * bx + by * by;
            double c_squared = cx * cx + cy * cy;

            return (a_squared * det_bc + b_squared * det_ca + c_squared * det_ab) > 0.0;*/

            double m1, m2;
            double mx1, mx2;
            double my1, my2;
            double xc, yc;

            if (System.Math.Abs(FirstPoint.Y - SecondPoint.Y) < float.Epsilon &&
                System.Math.Abs(SecondPoint.Y - ThirdPoint.Y) < float.Epsilon)
            {
                // INCIRCUM - F - Points are coincident !!
                return false;
            }

            if (System.Math.Abs(SecondPoint.Y - FirstPoint.Y) < float.Epsilon)
            {
                m2 = -(ThirdPoint.X - SecondPoint.X) / (ThirdPoint.Y - SecondPoint.Y);
                mx2 = (SecondPoint.X + ThirdPoint.X) * 0.5;
                my2 = (SecondPoint.Y + ThirdPoint.Y) * 0.5;
                // Calculate CircumCircle center (xc,yc)
                xc = (SecondPoint.X + FirstPoint.X) * 0.5;
                yc = m2 * (xc - mx2) + my2;
            }
            else if (System.Math.Abs(ThirdPoint.Y - SecondPoint.Y) < float.Epsilon)
            {
                m1 = -(SecondPoint.X - FirstPoint.X) / (SecondPoint.Y - FirstPoint.Y);
                mx1 = (FirstPoint.X + SecondPoint.X) * 0.5;
                my1 = (FirstPoint.Y + SecondPoint.Y) * 0.5;
                // Calculate CircumCircle center (xc,yc)
                xc = (ThirdPoint.X + SecondPoint.X) * 0.5;
                yc = m1 * (xc - mx1) + my1;
            }
            else
            {
                m1 = -(SecondPoint.X - FirstPoint.X) / (SecondPoint.Y - FirstPoint.Y);
                m2 = -(ThirdPoint.X - SecondPoint.X) / (ThirdPoint.Y - SecondPoint.Y);
                mx1 = (FirstPoint.X + SecondPoint.X) * 0.5;
                mx2 = (SecondPoint.X + ThirdPoint.X) * 0.5;
                my1 = (FirstPoint.Y + SecondPoint.Y) * 0.5;
                my2 = (SecondPoint.Y + ThirdPoint.Y) * 0.5;
                //Calculate CircumCircle center (xc,yc)
                xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
                yc = m1 * (xc - mx1) + my1;
            }

            double dx = SecondPoint.X - xc;
            double dy = SecondPoint.Y - yc;
            double rsqr = dx * dx + dy * dy;
            //double r = Math.Sqrt(rsqr); //Circumcircle radius
            dx = point.X - xc;
            dy = point.Y - yc;
            double drsqr = dx * dx + dy * dy;

            return (drsqr <= rsqr);
        }

        /// <summary>
        /// Tests if two triangles share at least one vertex.
        /// </summary>
        /// <param name="triangle">A other triangle<see cref="Triangle"/>.</param>
        /// <returns>Returns true if two triangles share at least one vertex, false otherwise.</returns>
        public bool SharesVertexWith(Triangle triangle)
        {
            if ((FirstPoint == triangle.FirstPoint || FirstPoint == triangle.SecondPoint || FirstPoint == triangle.ThirdPoint) ||
                (SecondPoint == triangle.FirstPoint || SecondPoint == triangle.SecondPoint || SecondPoint == triangle.ThirdPoint) ||
                (ThirdPoint == triangle.FirstPoint || ThirdPoint == triangle.SecondPoint || ThirdPoint == triangle.ThirdPoint))
                return true;

            return false;
        }

        #endregion Public Methods

        #region Public Properties

        /// <summary>
        /// Gets or sets the first point of the edge
        /// </summary>
        public Point3D FirstPoint
        {
            get { return firstPoint; }
            set { firstPoint = value; }
        }

        /// <summary>
        /// Gets or sets the second point of the edge
        /// </summary>
        public Point3D SecondPoint
        {
            get { return secondPoint; }
            set { secondPoint = value; }
        }

        /// <summary>
        /// Gets or sets the third point of the edge
        /// </summary>
        public Point3D ThirdPoint
        {
            get { return thirdPoint; }
            set { thirdPoint = value; }
        }

        #endregion Public Properties
    }
}
